#include <stdio.h>     /* for printf */
#include <stdlib.h>    /* for exit */
#include <errno.h>
#include <string.h>
#include "handle_opts.h"

int opts_to_string( OPTS_STRUCT* opts_p , unsigned int opt_count , char ** string_p ){

	int i , j=0 ;

	/* Getting enough space to fill ':' so we do '* 2' */
	unsigned int 	ret_str_length 		= sizeof(char) * opt_count * 2; 
	char * 		ret_str_content 	= (char *)malloc( ret_str_length );

	if( !ret_str_content ){
		Plog("can't allocate enough memory !!!" , EAGAIN );
		return -1;
	}
	memset( ret_str_content , 0 , ret_str_length );

	for( i=0 ; i<opt_count ; i++ ){
		
		if( '\0' == opts_p[i].short_opt_char ){
			continue;
		}
		
		ret_str_content[j] = opts_p[i].short_opt_char;
		j++;
		
		if( opts_p[i].has_arg ){
			ret_str_content[j] = ':';
			j++;
		}
	}

	*string_p = ret_str_content;

	return 0;
}

int opts_to_options( OPTS_STRUCT * opts_p , unsigned int opt_count , struct option ** option_p_p){
	
	int i;

	/* It need a line of all NULL to mark the end */
	unsigned int 	ret_option_length	= sizeof(struct option) * opt_count + 1 ; 
	struct option *	ret_option_content	= (struct option *)malloc( ret_option_length );
	if( !ret_option_content ){
		Plog("can't allocate enough memory !!!" , EAGAIN );
		return -1;
	}
	memset( ret_option_content , 0 , ret_option_length );
	
	for( i=0 ; i<opt_count ; i++ ){

		if( !opts_p[i].long_opt_string || !strlen(opts_p[i].long_opt_string) ){
			continue;
		}

		ret_option_content[i].name = opts_p[i].long_opt_string;

		if( opts_p[i].has_arg ){
			ret_option_content[i].has_arg	= opts_p[i].has_arg;
			ret_option_content[i].val	= opts_p[i].short_opt_char;
			ret_option_content[i].flag	= NULL;
		}
	}

	*option_p_p = ret_option_content;

	return 0 ;
}

int handle_opts(int argc , char ** argv , OPTS_STRUCT *opts , unsigned int opt_count ){
	
	struct option* long_options_p;
	int option_index , c ;
	char * string;
	int i;
	
	if( opts_to_string( opts , opt_count , &string ) < 0 ){
		printf(" opts_to_string failed!!!");
		exit(-1);
	}

	if( opts_to_options( opts , opt_count , &long_options_p ) < 0){
		printf("opts_to_option failed!!!");
		exit(-2);
	}

	while(1){
		c = getopt_long_only (argc, argv, string , long_options_p , &option_index);
		if (c == -1){
			break;
		}

		for( i=0 ; i<opt_count ; i++ ){

			if( !( 
				( 0 == c && ( !strcmp(opts[i].long_opt_string , long_options_p[option_index].name ) )) ||
				( 0 != c && (c == opts[i].short_opt_char) )
			    ) ){
				continue;
			}
			opts[i].ret_value.has_default = true;

			if( opts[i].has_arg ){
				switch( opts[i].opt_type ){
				case BOOL:
					if( ( !strcasecmp( optarg , "true") ) ||
					    ( !strcasecmp( optarg , "1") ) 
					){
						opts[i].ret_value.content.content_bool = true;
					}else{
						opts[i].ret_value.content.content_bool = false;
					}
					break;
				case INT:
					opts[i].ret_value.content.content_int = atoi( optarg );
					break;
				case FLOAT:
					opts[i].ret_value.content.content_float = atof( optarg );
					break;
				case STRING:
				default:
					opts[i].ret_value.content.content_string = optarg;
					break;
				}
			}
		}
		
	}
	for( i=0 ; i<opt_count ; i++ ){
		if( true != opts[i].ret_value.has_default ){
			return false;
		}
	}
	return true;
}
void print_args( OPTS_STRUCT opt ){
	if( !opt.ret_value.has_default ){
		return;
	}
	switch (opt.opt_type){
	case BOOL:
		printf( "TYPE: bool,   SHORT: -%c, LONG: --%s,\t DESCRIPTION: \"%s\" , VALUE: %s \n" , opt.short_opt_char , opt.long_opt_string , opt.instruction , opt.ret_value.content.content_bool?"true":"false" );
		break;
	case INT:
		printf( "TYPE: int,    SHORT: -%c, LONG: --%s,\t DESCRIPTION: \"%s\" , VALUE: %d \n" , opt.short_opt_char , opt.long_opt_string , opt.instruction , opt.ret_value.content.content_int );
		break;
	case FLOAT:
		printf( "TYPE: float,  SHORT: -%c, LONG: --%s,\t DESCRIPTION: \"%s\" , VALUE: %f \n" , opt.short_opt_char , opt.long_opt_string , opt.instruction , opt.ret_value.content.content_float );
		break;
	case STRING:
		printf( "TYPE: string, SHORT: -%c, LONG: --%s,\t DESCRIPTION: \"%s\" , VALUE: %s \n" , opt.short_opt_char , opt.long_opt_string , opt.instruction , opt.ret_value.content.content_string );
		break;
	default:
		Plog( "unknown argument type !!" , 0 );
		break;
	}
	return;
}

#if defined( __UNIT_TEST__)
OPTS_STRUCT opts[] = {
	{BOOL	, 'b' , "bool"	, "bool arg"	, true , {true , {.content_bool = false}   } 	},
	{INT  	, 'i' , "int" 	, "int arg"	, true , {false , {.content_int = 100} 	}	},
	{FLOAT	, 'f' , "float"	, "float arg"	, true , {false , {.content_float = 0.01f}  } 	},
	{STRING	, 's' , "string" , "string arg"	, true , {true , {.content_string = "hello"} } 	},
	{STRING ,'\0' , "null" ,  "null arg"   , true , {true , {.content_string = "it's null"}}},
	{INT    , 'T' , NULL , "TEST ARGS"    , true , {true , {.content_int = 10}}},
};
unsigned int opts_count = sizeof(opts)/sizeof( OPTS_STRUCT );

int main( int argc , char ** argv ){
	handle_opts( argc , argv , opts , opts_count );
	int i;
	for( i=0 ; i<opts_count ; i++){
		print_args(opts[i]);
	}
	return 0;
}
#endif
